<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="YWhUi" id="YWhUi"><span data-lake-id="u6ec85729" id="u6ec85729">典型回答</span></h1>
  <p data-lake-id="u286026d2" id="u286026d2"><span data-lake-id="u8a65857f" id="u8a65857f">Spring有着非常优雅的设计，很多地方都遵循SOLID原则，里面的设计模式更是数不胜数。大概有以下几种：</span></p>
  <h2 data-lake-id="gbUH9" id="gbUH9"><span data-lake-id="ub54840a2" id="ub54840a2">工厂模式</span></h2>
  <p data-lake-id="ue958ff20" id="ue958ff20"><span data-lake-id="ua1584328" id="ua1584328">所谓的工厂模式，核心是屏蔽内部的实现，直接由client使用即可。定义可以参考：</span></p>
  <p data-lake-id="u926798cb" id="u926798cb"><span data-lake-id="u6a0a0374" id="u6a0a0374">Spring的IOC就是一个非常好的工厂模式的例子。Spring IOC 容器就像是一个工厂一样，当我们需要创建一个对象的时候，只需要配置好配置文件/注解即可，完全不用考虑对象是如何被创建出来的。 IOC 容器负责创建对象，将对象连接在一起，配置这些对象，并从创建中处理这些对象的整个生命周期，直到它们被完全销毁。</span></p>
  <h2 data-lake-id="jJSIK" id="jJSIK"><span data-lake-id="ufd99e8ae" id="ufd99e8ae">组合模式</span></h2>
  <p data-lake-id="u8493d853" id="u8493d853"><span data-lake-id="u039bdea3" id="u039bdea3">组合模式在SpringMVC中用的非常多，其中的参数解析，响应值处理等模块就是使用了组合模式。拿参数解析模块举例：</span></p>
  <p data-lake-id="u227cd8d4" id="u227cd8d4"><span data-lake-id="u1255f532" id="u1255f532">类图如下：</span></p>
  <p data-lake-id="u0df4bddf" id="u0df4bddf"><img src="https://cdn.nlark.com/yuque/0/2023/png/719664/1684339968357-8cd8cacd-3837-491f-a86c-d5d07451b85f.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_48%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u04900b56" id="u04900b56"><span data-lake-id="ue88a8295" id="ue88a8295">可以发现，整体的参数解析模块中，由一个接口</span><code data-lake-id="ua58cc1bc" id="ua58cc1bc"><span data-lake-id="u5c843b19" id="u5c843b19">HandlerMethodArgumentResolver</span></code><span data-lake-id="u301c1f41" id="u301c1f41">负责。其中父节点会实现该接口，同时对所有的具体的子接口进行聚合。</span></p>
  <p data-lake-id="ufc8488ae" id="ufc8488ae"><span data-lake-id="u19129e54" id="u19129e54">其实这个里面不止用了组合模式，接口还提供了</span><code data-lake-id="uaa59e477" id="uaa59e477"><span data-lake-id="ua5b94afc" id="ua5b94afc">#supportsParamerter</span></code><span data-lake-id="ufe0e6fca" id="ufe0e6fca">方法，去判断是否执行该resolver，这也是策略模式的一种。</span></p>
  <h2 data-lake-id="AqzUC" id="AqzUC"><span data-lake-id="u7f77d676" id="u7f77d676">适配器模式</span></h2>
  <p data-lake-id="u311956fe" id="u311956fe"><span data-lake-id="ue5fec7b0" id="ue5fec7b0">适配器模式简而言之就是上游为了适应下游，而要做一些适配，承担适配工作的模块，就叫做适配器。常见的场景是甲方因为话语权很高，提供了一套交互模型，而所有对接甲方模型的乙方，就需要通过适配器模式来适配甲方的模型和自己已有的系统。</span></p>
  <p data-lake-id="u7ebd972a" id="u7ebd972a"><span data-lake-id="uc13f2292" id="uc13f2292">在SpringMVC中，</span><code data-lake-id="u7815b4b3" id="u7815b4b3"><span data-lake-id="u26dc43d2" id="u26dc43d2">HandlerAdapter</span></code><span data-lake-id="u3ee5421f" id="u3ee5421f">就是典型的适配器模式。参考其注释我们可以发现：</span></p>
  <blockquote data-lake-id="uc224ff20" id="uc224ff20">
   <p data-lake-id="ufb2f6df8" id="ufb2f6df8"><span data-lake-id="uc55c2485" id="uc55c2485">MVC framework SPI, allowing parameterization of the core MVC workflow.</span></p>
   <p data-lake-id="ued905620" id="ued905620"><span data-lake-id="ubdb3c926" id="ubdb3c926">Interface that must be implemented for each handler type to handle a request. This interface is used to allow the DispatcherServlet to be indefinitely extensible. The DispatcherServlet accesses all installed handlers through this interface, meaning that it does not contain code specific to any handler type.</span></p>
  </blockquote>
  <p data-lake-id="u6f768b5f" id="u6f768b5f"><span data-lake-id="u57b148eb" id="u57b148eb">对于DispatcherServlet来说，HandlerAdapter是核心的业务逻辑处理流程，DispatcherServlet只负责调用</span><code data-lake-id="u2186da6b" id="u2186da6b"><span data-lake-id="u22c4515f" id="u22c4515f">HandlerAdapter#handle</span></code><span data-lake-id="u40c5725a" id="u40c5725a">方法即可。至于当前Http的请求该如何处理，则交给HandlerAdapter的实现方负责。换句话说，HandlerAdapter只是定义了和DispatcherServlet交互的标准，帮助不同的实现适配了DispatcherServlet而已。</span></p>
  <p data-lake-id="uf9962138" id="uf9962138"><span data-lake-id="ua29a7e63" id="ua29a7e63">譬如，用于Controller注解解析和url映射的逻辑就是通过</span><code data-lake-id="u21d1dbbe" id="u21d1dbbe"><span data-lake-id="udf55bed0" id="udf55bed0">RequestMappingHandlerAdapter</span></code><span data-lake-id="ue4c60d6d" id="ue4c60d6d">实现的。</span></p>
  <pre lang="java"><code>
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    

    try {
        ModelAndView mv = null;
        Exception dispatchException = null;

        try {

            // Determine handler for the current request.
            mappedHandler = getHandler(processedRequest);

            // Determine handler adapter for the current request.
            HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();

            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

            // Actually invoke the handler. 【重要】
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        }
        catch (Exception ex) {
            dispatchException = ex;
        }
        catch (Throwable err) {
            // As of 4.3, we're processing Errors thrown from handler methods as well,
            // making them available for @ExceptionHandler methods and other scenarios.
            dispatchException = new NestedServletException("Handler dispatch failed", err);
        }
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
    }
    finally {
    }
}
</code></pre>
  <p data-lake-id="ue4b7bb0c" id="ue4b7bb0c"><span data-lake-id="u00e01d2f" id="u00e01d2f">​</span><br></p>
  <h2 data-lake-id="gN9V9" id="gN9V9"><span data-lake-id="u425874e5" id="u425874e5">代理模式</span></h2>
  <p data-lake-id="ue62fc7a4" id="ue62fc7a4"><span data-lake-id="u35dde016" id="u35dde016">代理模式和适配器模式的核心区别就在于，适配器模式的目的是为了适配不同的场景，而代理模式的目的则是enhance，即增强被代理的类（如增加日志打印功能等）。</span></p>
  <p data-lake-id="u857fa600" id="u857fa600"><span data-lake-id="u5dac447f" id="u5dac447f">Spring的AOP就是代理模式的典型代表，请参考：</span></p>
  <h2 data-lake-id="GlFBK" id="GlFBK"><span data-lake-id="u09265a10" id="u09265a10">单例模式</span></h2>
  <p data-lake-id="uf28ff7a3" id="uf28ff7a3"><span data-lake-id="uf6d41cb8" id="uf6d41cb8">单例模式是Spring一个非常核心的功能，Spring中的bean默认都是单例的，这样可以尽最大程度保证</span><strong><span data-lake-id="u2559f4ad" id="u2559f4ad">对象的复用和线程安全</span></strong><span data-lake-id="ua5ba6709" id="ua5ba6709">。</span></p>
  <p data-lake-id="u2a506bd1" id="u2a506bd1"><span data-lake-id="u6f71df60" id="u6f71df60">Spring Bean也不止是单例的，还有其他作用域，如下：</span></p>
  <ul list="u89f7a48a">
   <li fid="u94efc0c7" data-lake-id="u737c9317" id="u737c9317"><strong><span data-lake-id="u67779165" id="u67779165">prototype</span></strong><span data-lake-id="u5e319888" id="u5e319888"> : 每次获取都会创建一个新的 bean 实例。也就是说，连续 </span><span data-lake-id="u93768be3" id="u93768be3">getBean()</span><span data-lake-id="u1d32121f" id="u1d32121f"> 两次，得到的是不同的 Bean 实例。</span></li>
   <li fid="u94efc0c7" data-lake-id="u671e7a1e" id="u671e7a1e"><strong><span data-lake-id="u9180de1c" id="u9180de1c">request</span></strong><span data-lake-id="ueeab22cf" id="ueeab22cf"> （仅 Web 应用可用）: 每一次 HTTP 请求都会产生一个新的 bean（请求 bean），该 bean 仅在当前 HTTP request 内有效。</span></li>
   <li fid="u94efc0c7" data-lake-id="u3ae07fde" id="u3ae07fde"><strong><span data-lake-id="u2189e631" id="u2189e631">session</span></strong><span data-lake-id="ue797b303" id="ue797b303"> （仅 Web 应用可用） : 每一次来自新 session 的 HTTP 请求都会产生一个新的 bean（会话 bean），该 bean 仅在当前 HTTP session 内有效。</span></li>
   <li fid="u94efc0c7" data-lake-id="ufd3c0923" id="ufd3c0923"><strong><span data-lake-id="u18a3586e" id="u18a3586e">global-session</span></strong><span data-lake-id="u1366fec6" id="u1366fec6"> （仅 Web 应用可用）：每个 Web 应用在启动时创建一个 Bean（应用 Bean），，该 bean 仅在当前应用启动时间内有效。</span></li>
   <li fid="u94efc0c7" data-lake-id="uac16eb63" id="uac16eb63"><strong><span data-lake-id="udeb41185" id="udeb41185">websocket</span></strong><span data-lake-id="u85ff8291" id="u85ff8291"> （仅 Web 应用可用）：每一次 WebSocket 会话产生一个新的 bean。</span></li>
  </ul>
  <p data-lake-id="u929b3a7d" id="u929b3a7d"><img src="https://cdn.nlark.com/yuque/0/2023/webp/719664/1683644933190-9c820c78-2067-4459-9bdb-f398248c3ab1.webp?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_16%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <h2 data-lake-id="EvYT1" id="EvYT1"><span data-lake-id="ua64efa92" id="ua64efa92">观察者模式</span></h2>
  <p data-lake-id="uf74eb1e8" id="uf74eb1e8"><span data-lake-id="ua0a19dd7" id="ua0a19dd7">具体可以参考：</span></p>
  <p data-lake-id="ubff98d12" id="ubff98d12"><span data-lake-id="u2c226fec" id="u2c226fec">​</span><br></p>
  <h2 data-lake-id="y3FOv" id="y3FOv"><span data-lake-id="ueb738112" id="ueb738112">模板方法模式</span></h2>
  <p data-lake-id="u7c00b2dc" id="u7c00b2dc"><span data-lake-id="u9aeb316e" id="u9aeb316e">如果使用过Spring的事务管理，相信一定对</span><code data-lake-id="u752f1905" id="u752f1905"><span data-lake-id="uaf5e249d" id="uaf5e249d">TransactionTemplate&#x0;</span></code><span data-lake-id="u0cc10824" id="u0cc10824">这个类不陌生，而且顾名思义，这个也是用到了模板方法。它把事务操作按照3个固定步骤来写：</span></p>
  <ol list="u549e9cd4">
   <li fid="u1eeffc1f" data-lake-id="u331dbfb4" id="u331dbfb4"><span data-lake-id="u23456dd9" id="u23456dd9">执行业务逻辑</span></li>
   <li fid="u1eeffc1f" data-lake-id="ue7fb8d9b" id="ue7fb8d9b"><span data-lake-id="ua8ab6ffc" id="ua8ab6ffc">如果异常则回滚事务</span></li>
   <li fid="u1eeffc1f" data-lake-id="u6c10e4d4" id="u6c10e4d4"><span data-lake-id="u0d45587e" id="u0d45587e">否则提交事务</span></li>
  </ol>
  <p data-lake-id="u0aee7251" id="u0aee7251"><span data-lake-id="ue8835b77" id="ue8835b77">如下代码所示：</span></p>
  <pre lang="java"><code>
public &lt;T&gt; T execute(TransactionCallback&lt;T&gt; action) throws TransactionException {

    TransactionStatus status = this.transactionManager.getTransaction(this);
    T result;
    try {
        // 1. 步骤一，执行事务逻辑
        result = action.doInTransaction(status);
    }
    catch (RuntimeException | Error ex) {
        // Transactional code threw application exception -&gt; rollback
        rollbackOnException(status, ex);
        throw ex;
    }
    catch (Throwable ex) {
        // Transactional code threw unexpected exception -&gt; rollback
        rollbackOnException(status, ex);
        throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
    }
    // 2. 步骤三，提交事务
    this.transactionManager.commit(status);
    return result;
    
}
</code></pre>
  <h2 data-lake-id="vnRni" id="vnRni"><span data-lake-id="u61d04ec9" id="u61d04ec9">责任链模式</span></h2>
  <p data-lake-id="u03214a5b" id="u03214a5b"><span data-lake-id="u2a622ee0" id="u2a622ee0">对于SpringMVC来说，他会通过一系列的拦截器来处理请求执行前，执行后，以及结束的response，核心的类是</span><code data-lake-id="ucbb392b0" id="ucbb392b0"><span data-lake-id="uac0a81b8" id="uac0a81b8">handlerExecutionChain</span></code><span data-lake-id="u4a4071d4" id="u4a4071d4">，它封装了</span><code data-lake-id="ufd09e501" id="ufd09e501"><span data-lake-id="u070eb866" id="u070eb866">HandlerAdapter</span></code><span data-lake-id="u4abc3e67" id="u4abc3e67">和一系列的过滤器。</span></p>
  <p data-lake-id="u6978e6b5" id="u6978e6b5"><span data-lake-id="u7d73fefb" id="u7d73fefb">对于执行前的处理来说，DispatherServlet会先通过</span><code data-lake-id="u017f7fb6" id="u017f7fb6"><span data-lake-id="u94c65c8b" id="u94c65c8b">handlerExecutionChain</span></code><span data-lake-id="u2781bbf3" id="u2781bbf3">获取所有的</span><code data-lake-id="uc1c5ded6" id="uc1c5ded6"><span data-lake-id="u3fff7791" id="u3fff7791">HandlerInterceptor</span></code><span data-lake-id="ue473f6b0" id="ue473f6b0">，然后再执行处理逻辑，如下代码所示：</span></p>
  <pre lang="java"><code>
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    try {

        try {

            // Process last-modified header, if supported by the handler.
            String method = request.getMethod();
        	// 执行预处理
            if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                return;
            }

        }
        processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    }
    catch (Exception ex) {
    }
    finally {
    }
}
</code></pre>
  <pre lang="java"><code>
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = 0; i &lt; interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            if (!interceptor.preHandle(request, response, this.handler)) {
                triggerAfterCompletion(request, response, null);
                return false;
            }
            this.interceptorIndex = i;
        }
    }
    return true;
}
</code></pre>
 </body>
</html>